///PHILOTES Source Code.  (C)2012 PhiloLabs
#pragma once

#ifndef __ANIMABLE_H__
#define __ANIMABLE_H__

#include "OgrePrerequisites.h"
#include "math/vector2.h"
#include "math/vector3.h"
#include "math/vector4.h"
#include "math/quaternion.h"
#include "util/colourValue.h"
#include "OgreSharedPtr.h"
#include "OgreStringVector.h"
#include "core/exception.h"
#include "OgreAny.h"

namespace Ogre {
	

	

	
	class _OgreExport AnimableValue : public MemAlloc_Animation
	{
	public:
		/// The type of the value being animated
		enum ValueType
		{
			INT,
			REAL,
			VECTOR2,
			VECTOR3,
			VECTOR4,
			QUATERNION,
			COLOUR,
			RADIAN,
			DEGREE
		};
	protected:
		/// Value type
		ValueType mType;

		/// Base value data
		union
		{
			int mBaseValueInt;
			Real mBaseValueReal[4];
		};

		/// Internal method to set a value as base
		virtual void setAsBaseValue(int val) { mBaseValueInt = val; }
		/// Internal method to set a value as base
		virtual void setAsBaseValue(Real val) { mBaseValueReal[0] = val; }
		/// Internal method to set a value as base
		virtual void setAsBaseValue(const Vector2& val) 
		{ memcpy(mBaseValueReal, val.ptr(), sizeof(Real)*2); }
		/// Internal method to set a value as base
		virtual void setAsBaseValue(const Vector3& val) 
		{ memcpy(mBaseValueReal, val.ptr(), sizeof(Real)*3); }
		/// Internal method to set a value as base
		virtual void setAsBaseValue(const Vector4& val) 
		{ memcpy(mBaseValueReal, val.ptr(), sizeof(Real)*4); }
		/// Internal method to set a value as base
		virtual void setAsBaseValue(const Quaternion& val) 
		{ memcpy(mBaseValueReal, val.ptr(), sizeof(Real)*4); }
		/// Internal method to set a value as base
		virtual void setAsBaseValue(const Any& val);
		/// Internal method to set a value as base
		virtual void setAsBaseValue(const Util::ColourValue& val)
		{ 
			mBaseValueReal[0] = val.r;
			mBaseValueReal[1] = val.g;
			mBaseValueReal[2] = val.b;
			mBaseValueReal[3] = val.a;
		}
		/// Internal method to set a value as base
		virtual void setAsBaseValue(const Radian& val)
		{ 
			mBaseValueReal[0] = val.valueRadians();
		}
		/// Internal method to set a value as base
		virtual void setAsBaseValue(const Degree& val)
		{ 
			mBaseValueReal[0] = val.valueRadians();
		}


	public:
		AnimableValue(ValueType t) : mType(t) {}
		virtual ~AnimableValue() {}

		/// Gets the value type of this animable value
		ValueType getType(void) const { return mType; }

		/// Sets the current state as the 'base' value; used for delta animation
		virtual void setCurrentStateAsBaseValue(void) = 0;

		/// Set value 
		virtual void setValue(int) {
			PHILO_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "", "");
		}
		/// Set value 
		virtual void setValue(Real) {
			PHILO_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "", "");
		}
		/// Set value 
		virtual void setValue(const Vector2&) {
			PHILO_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "", "");
		}
		/// Set value 
		virtual void setValue(const Vector3&) {
			PHILO_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "", "");
		}
		/// Set value 
		virtual void setValue(const Vector4&) {
			PHILO_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "", "");
		}
		/// Set value 
		virtual void setValue(const Quaternion&) {
			PHILO_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "", "");
		}
		/// Set value 
		virtual void setValue(const ColourValue&) {
			PHILO_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "", "");
		}
		/// Set value 
		virtual void setValue(const Radian&) {
			PHILO_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "", "");
		}
		/// Set value 
		virtual void setValue(const Degree&) {
			PHILO_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "", "");
		}
		/// Set value 
		virtual void setValue(const Any& val);

		// reset to base value
		virtual void resetToBaseValue(void);

		/// Apply delta value
		virtual void applyDeltaValue(int) {
			PHILO_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "", "");
		}
		/// Set value 
		virtual void applyDeltaValue(Real) {
			PHILO_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "", "");
		}
		/// Apply delta value 
		virtual void applyDeltaValue(const Vector2&) {
			PHILO_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "", "");
		}
		/// Apply delta value 
		virtual void applyDeltaValue(const Vector3&) {
			PHILO_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "", "");
		}
		/// Apply delta value 
		virtual void applyDeltaValue(const Vector4&) {
			PHILO_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "", "");
		}
		/// Apply delta value 
		virtual void applyDeltaValue(const Quaternion&) {
			PHILO_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "", "");
		}
		/// Apply delta value 
		virtual void applyDeltaValue(const ColourValue&) {
			PHILO_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "", "");
		}
		/// Apply delta value 
		virtual void applyDeltaValue(const Degree&) {
			PHILO_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "", "");
		}
		/// Apply delta value 
		virtual void applyDeltaValue(const Radian&) {
			PHILO_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "", "");
		}
		/// Apply delta value 
		virtual void applyDeltaValue(const Any& val);


	};

	typedef SharedPtr<AnimableValue> AnimableValuePtr;



	
	class _OgreExport AnimableObject
	{
	protected:
		typedef map<String, StringVector>::type AnimableDictionaryMap;
		/// Static map of class name to list of animable value names
		static AnimableDictionaryMap msAnimableDictionary;
		
		virtual const String& getAnimableDictionaryName(void) const 
		{ return StringUtil::BLANK; }
        
        void createAnimableDictionary(void) const
        {
            if (msAnimableDictionary.find(getAnimableDictionaryName()) 
				== msAnimableDictionary.end())
            {
				StringVector vec;
				initialiseAnimableDictionary(vec);
				msAnimableDictionary[getAnimableDictionaryName()] = vec;
            }

        }
	
		/// Get an updateable reference to animable value list
		StringVector& _getAnimableValueNames(void)
		{
			AnimableDictionaryMap::iterator i = 
				msAnimableDictionary.find(getAnimableDictionaryName());
			if (i != msAnimableDictionary.end())
			{
				return i->second;
			}
			else
			{
				PHILO_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, 
					"Animable value list not found for " + getAnimableDictionaryName(), 
					"AnimableObject::getAnimableValueNames");
			}

		}

		
		virtual void initialiseAnimableDictionary(StringVector&) const {}


	public:
		AnimableObject() {}
		virtual ~AnimableObject() {}

		
		const StringVector& getAnimableValueNames(void) const
		{
			createAnimableDictionary();

			AnimableDictionaryMap::iterator i = 
				msAnimableDictionary.find(getAnimableDictionaryName());
			if (i != msAnimableDictionary.end())
			{
				return i->second;
			}
			else
			{
				PHILO_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, 
					"Animable value list not found for " + getAnimableDictionaryName(), 
					"AnimableObject::getAnimableValueNames");
			}

		}

		
		virtual AnimableValuePtr createAnimableValue(const String& valueName)
		{
			PHILO_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, 
				"No animable value named '" + valueName + "' present.", 
				"AnimableObject::createAnimableValue");
		}



	};

	
	

}
#endif

